package com.shanyin.framework.common.util;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.shanyin.framework.common.base.RedissLock;

/***
 ** @category Redission分布式工具类...
 ** @author qing.yunhui
 ** @email: 280672161@qq.com
 ** @createTime: 2018年5月28日下午5:10:50
 **/
@Component
public class RedissLockUtil {

	private static final Logger logger = LoggerFactory.getLogger(RedissLockUtil.class);  
    private final static long LOCK_EXPIRE = 30 * 1000L;// 单个业务持有锁的时间30s，防止死锁  
    private final static long LOCK_TRY_INTERVAL = 30L;// 默认30ms尝试一次  
    private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;// 默认尝试20s  
  
    @Autowired  
    private StringRedisTemplate template;  
  
    /** 
     * 尝试获取全局锁 
     * @param lock 锁的名称 
     * @return true 获取成功，false获取失败 
     */  
    public boolean tryLock(RedissLock lock) {  
        return getLock(lock, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_EXPIRE);  
    }  
  
    /** 
     * 尝试获取全局锁 
     * @param lock 锁的名称 
     * @param timeout  获取超时时间 单位ms 
     * @return true 获取成功，false获取失败 
     */  
    public boolean tryLock(RedissLock lock, long timeout) {  
        return getLock(lock, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);  
    }  
  
    /** 
     * 尝试获取全局锁 
     * @param lock  锁的名称 
     * @param timeout  获取锁的超时时间 
     * @param tryInterval  多少毫秒尝试获取一次 
     * @return true 获取成功，false获取失败 
     */  
    public boolean tryLock(RedissLock lock, long timeout, long tryInterval) {  
        return getLock(lock, timeout, tryInterval, LOCK_EXPIRE);  
    }  
  
    /** 
     * 尝试获取全局锁 
     * @param lock  锁的名称 
     * @param timeout  获取锁的超时时间 
     * @param tryInterval 多少毫秒尝试获取一次 
     * @param lockExpireTime 锁的过期 
     * @return true 获取成功，false获取失败 
     */  
    public boolean tryLock(RedissLock lock, long timeout, long tryInterval, long lockExpireTime) {  
        return getLock(lock, timeout, tryInterval, lockExpireTime);  
    }  
  
    /** 
     * 操作redis获取全局锁 
     * @param lock  锁的名称 
     * @param timeout  获取的超时时间 
     * @param tryInterval  多少ms尝试一次 
     * @param lockExpireTime  获取成功后锁的过期时间 
     * @return true 获取成功，false获取失败 
     */  
    public boolean getLock(RedissLock lock, long timeout, long tryInterval, long lockExpireTime) {  
        try {  
            if (StringUtil.isEmptys(lock,lock.getName(),lock.getValue())) return false;   
            long startTime = System.currentTimeMillis();  
            do {  
                if (!template.hasKey(lock.getName())) {  
                    ValueOperations<String, String> ops = template.opsForValue();  
                    ops.set(lock.getName(), lock.getValue(), lockExpireTime, TimeUnit.MILLISECONDS);  
                    return true;  
                } else {// 存在锁  
                    logger.info("=====================> redissLock = {} is exist!！！",new Object[] {JSONObject.toJSONString(lock)});  
                }  
                if (System.currentTimeMillis() - startTime > timeout) {// 尝试超过了设定值之后直接跳出循环  
                    return false;  
                }  
                Thread.sleep(tryInterval);  
            } while (template.hasKey(lock.getName()));  
        } catch (InterruptedException e) {  
            logger.error("=====================> getLock error.",e.getMessage());  
            return false;  
        }  
        return false;  
    }  
  
    /** 
     * 释放锁 
     */  
    public void releaseLock(RedissLock lock) {  
        if (!StringUtil.isEmptys(lock.getName())) {  
            template.delete(lock.getName());  
        }  
    }  
	
}
